/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.camera.ui;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.drawable.Drawable;

import com.android.camera.util.Gusterpolator;
import com.android.camera2.R;

/**
 * This class implements a circular drawable that starts with a zero radius
 * and can be triggered to animate expand to a given radius.
 * <p>
 * There are two colors associated with this drawable:
 * <p>
 * A background color, which is loaded from a resource
 * R.color.mode_icon_hover_highlight.
 * <p>
 * And, a main color, which is attached to the main circle that is expanded last
 * and is drawn on top of the other colors.
 * <p>
 * The driving purpose for this class is to implement a Material-like look and
 * feel for mode switcher touch events.
 */
public class TouchCircleDrawable extends Drawable
{
    private static final int CIRCLE_ANIM_DURATION_MS = 250;

    private Paint mColorPaint = new Paint();
    private Paint mBackgroundPaint = new Paint();
    private int mColor;
    private int mColorAlpha = 0xff;
    private int mColorRadius;
    private int mBackgroundRadius;
    private Drawable mIconDrawable;
    private int mIconDrawableSize;
    private boolean mDrawBackground;

    private Animator.AnimatorListener mAnimatorListener;
    private ValueAnimator.AnimatorUpdateListener mUpdateListener;

    private static final int INVALID = -1;
    private int mW = INVALID;
    private int mH = INVALID;
    private Point mCenter;

    /**
     * Constructor
     *
     * @param resources Resources, needed to poke around for the background
     *                  color value.
     * @param color     The main this circle drawable expands to.
     * @param baseColor The color of the initial expanded circle
     *                  (draws behind the main color).
     */
    public TouchCircleDrawable(Resources resources, int color, int baseColor)
    {
        super();

        mColorPaint.setAntiAlias(true);
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setColor(resources.getColor(R.color.mode_icon_hover_highlight));

        setColor(color);
    }

    /**
     * Constructor
     *
     * @param resources Resources, needed to poke around for the background color value.
     */
    public TouchCircleDrawable(Resources resources)
    {
        this(resources, 0xffffff, 0xffffff);
    }

    /**
     * Set the size of this drawable.
     *
     * @param w Width to set.
     * @param h Height to set.
     */
    public void setSize(int w, int h)
    {
        mW = w;
        mH = h;
    }

    /**
     * Set the center of the circle for this drawable.
     *
     * @param p The center point.
     */
    public void setCenter(Point p)
    {
        mCenter = p;
        updateIconBounds();
    }

    /**
     * @return The center of this drawable.
     */
    public Point getCenter()
    {
        return mCenter;
    }

    @Override
    public void draw(Canvas canvas)
    {
        int w = mW;
        int h = mH;

        if (w == INVALID || h == INVALID || mCenter == null)
        {
            return;
        }

        if (mDrawBackground)
        {
            canvas.drawCircle(mCenter.x, mCenter.y, mBackgroundRadius, mBackgroundPaint);
        }
        canvas.drawCircle(mCenter.x, mCenter.y, mColorRadius, mColorPaint);
        if (mIconDrawable != null)
        {
            mIconDrawable.draw(canvas);
        }
    }

    @Override
    public void setAlpha(int alpha)
    {
        mColorAlpha = alpha;
    }

    @Override
    public void setColorFilter(ColorFilter cf)
    {
        mColorPaint.setColorFilter(cf);
    }

    @Override
    public int getOpacity()
    {
        return PixelFormat.TRANSLUCENT;
    }

    /**
     * Set the main color.
     *
     * @param color The main color.
     */
    public void setColor(int color)
    {
        mColor = color;
        mColorPaint.setColor(mColor);
        mColorPaint.setAlpha(mColorAlpha);
    }

    public void setIconDrawable(Drawable d, int size)
    {
        mIconDrawable = d;
        mIconDrawableSize = size;
        updateIconBounds();
    }

    private void updateIconBounds()
    {
        if (mCenter != null)
        {
            mIconDrawable.setBounds(
                    mCenter.x - mIconDrawableSize / 2, mCenter.y - mIconDrawableSize / 2,
                    mCenter.x + mIconDrawableSize / 2, mCenter.y + mIconDrawableSize / 2);
        }
    }

    /**
     * Start the expand animation.
     */
    public void animate()
    {
        mBackgroundRadius = Math.min(mW / 2, mH / 2);

        final ValueAnimator colorAnimator =
                ValueAnimator.ofInt(0, Math.min(mW / 2, mH / 2));
        colorAnimator.setDuration(CIRCLE_ANIM_DURATION_MS);
        colorAnimator.setInterpolator(Gusterpolator.INSTANCE);
        colorAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
        {
            @Override
            public void onAnimationUpdate(ValueAnimator animation)
            {
                mColorRadius = (Integer) animation.getAnimatedValue();
                invalidateSelf();
                if (mUpdateListener != null)
                {
                    mUpdateListener.onAnimationUpdate(animation);
                }
            }
        });

        colorAnimator.addListener(new AnimatorListener()
        {
            @Override
            public void onAnimationStart(Animator animation)
            {
                mDrawBackground = true;

                if (mAnimatorListener != null)
                {
                    mAnimatorListener.onAnimationStart(animation);
                }
            }

            @Override
            public void onAnimationEnd(Animator animation)
            {
                mDrawBackground = false;

                if (mAnimatorListener != null)
                {
                    mAnimatorListener.onAnimationEnd(animation);
                }
            }

            @Override
            public void onAnimationCancel(Animator animation)
            {
                mDrawBackground = false;

                if (mAnimatorListener != null)
                {
                    mAnimatorListener.onAnimationCancel(animation);
                }
            }

            @Override
            public void onAnimationRepeat(Animator animation)
            {
                if (mAnimatorListener != null)
                {
                    mAnimatorListener.onAnimationRepeat(animation);
                }
            }
        });

        colorAnimator.start();
    }

    /**
     * Reset this drawable to its initial, preanimated state.
     */
    public void reset()
    {
        mColorRadius = 0;
    }

    /**
     * Set an {@link android.animation.Animator.AnimatorListener} to be
     * attached to the animation.
     *
     * @param listener The listener.
     */
    public void setAnimatorListener(Animator.AnimatorListener listener)
    {
        mAnimatorListener = listener;
    }

    /**
     * Set an {@link android.animation.ValueAnimator} to be
     * attached to the animation.
     *
     * @param listener The listener.
     */
    public void setUpdateListener(ValueAnimator.AnimatorUpdateListener listener)
    {
        mUpdateListener = listener;
    }
}
